package com.chinatechstar.component.commons.utils;

import java.awt.image.BufferedImage;
import java.io.*;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;

import javax.annotation.PostConstruct;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;

import cn.hutool.core.img.Img;
import cn.hutool.core.img.ImgUtil;
import com.chinatechstar.admin.entity.SysUser;
import com.chinatechstar.admin.mapper.SysUserMapper;
import com.chinatechstar.component.commons.exception.AjaxExceptionEnum;
import com.chinatechstar.file.converter.SysFileHistoryConverter;
import com.chinatechstar.file.dto.CurrDirFileMapDTO;
import com.chinatechstar.file.dto.SysFileHistoryDTO;
import com.chinatechstar.file.entity.*;
import com.chinatechstar.file.mapper.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ResourceUtils;

import com.chinatechstar.component.commons.exception.ServiceException;
import org.springframework.web.multipart.MultipartFile;

/**
 * 文件工具类
 * Rei:尽量做到先验证或操作数据，再实际操作物理磁盘
 */
@Component
public class FileUtils {

    private static final SequenceGenerator SEQUENCE_GENERATOR = new SequenceGenerator();

    private static final String DEFAULT_STORAGE = "796218703213862912";    // 默认仓储
    private static final String SCI_RESEARCH = "795832461770989568";       // 科研项目
    private static final String OUTSOURCE_PRJ = "796214079195029504";      // 外协项目
    private static final String MECHANISM_SEARCH = "796214414441553920";   // 机理研究
    private static final String TECHNOLOGY_RESEARCH = "796217632550658048";// 工艺研究
    private static final String TANGIBLE_RESULTS = "796217827589988352";   // 有形化成果
    private static final String NEW_PROGRESS = "796218053579087872";       // 国内外研究
    private static final String LAWS_REGULATIONS = "796218499689455616";   // 国家政策法规
    private static final String GENERAL_SCHEMA = "796219099852414976";     // 注气总方案
    private static final String WELL_DESIGN = "796219294279376896";        // 单井设计
    private static final String EXPM_PLAN = "796219576316960768";          // 新工艺新技术
    private static final String EVENT_SUMMARY = "796219731057418240";      // 大事件纪要
    private static final String EXTENSION_DIR = "dir";
    private static final String PRJMGT = "prjmgt";                          // 科研项目管理
    private static final String OTHER_BUSINESS = "other";                   // 其他不需要工作流的业务

    private static final int IS_TRUE = 1;
    private static final int IS_FALSE = 0;
    private static final int LAUNCH_AUDIT_0 = 0;
    private static final int LAUNCH_AUDIT_1 = 1;
    private static final int IS_PASS_0 = 0;
    private static final int IS_PASS_1 = 1;
    private static final int IS_PASS_2 = 2;

    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private SysFileMapper sysFileMapper;
    @Autowired
    private SysFileBusinessMapper sysFileBusinessMapper;
    @Autowired
    private SysFileStorageMapper sysFileStorageMapper;
    @Autowired
    private SysFileHistoryMapper sysFileHistoryMapper;
    @Autowired
    private SysUserFileMapper sysUserFileMapper;
    @Autowired
    private SysUserGroupFileMapper sysUserGroupFileMapper;

    private static FileUtils fileUtils;

    private FileUtils() {

    }

    @PostConstruct
    public void init() {
        fileUtils = this;
    }

    public static String getPath() {
        return FileUtils.class.getResource("/").getPath();
    }
    /**
     * 下载文件
     *
     * @param originalFilename 文件名称
     * @param url              下载URL
     * @param response         响应对象
     */
    public static void downloadFile(String originalFilename, String url, HttpServletResponse response) throws IOException {
        ServletOutputStream servletOutputStream = null;
        try {
            servletOutputStream = response.getOutputStream();
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + originalFilename);
            response.setContentType("application/octet-stream");
            File rootDirectoryPath = new File(ResourceUtils.getURL("classpath:").getPath());
            String path = rootDirectoryPath + url;
            File file = new File(path);
            if (!file.exists()) {
                throw new FileNotFoundException("找不到此文件： " + originalFilename);
            }
            byte[] bytes = Files.readAllBytes(Paths.get(path));
            servletOutputStream.write(bytes);
            servletOutputStream.flush();
        } catch (Exception e) {
            throw new ServiceException(e.toString());
        } finally {
            if (servletOutputStream != null) {
                servletOutputStream.close();
            }
        }
    }

    /**
     * 根据id下载文件
     *
     * @param id       文件业务关联 ID
     * @param response HTTP相应消息
     */
    public static void download(Long id, HttpServletResponse response) {
        // 验证文件业务
        SysFileBusiness fileBusinessEntity = fileUtils.sysFileBusinessMapper.findSysFileBusinessById(id);

        String filePath;
        String fileName;
        String fileOriginalName;
        Long fileStorageId;

        // 验证文件
        SysFile fileEntity;
        SysFileHistory sysFileHistory;
        if (fileBusinessEntity == null) {
            sysFileHistory = validateFileHistory(id);
            filePath = sysFileHistory.getFilePath();
            fileName = sysFileHistory.getFileName();
            fileStorageId = sysFileHistory.getFileFilestorageId();
            fileOriginalName = sysFileHistory.getFileOriginalName();
        } else {
            fileEntity = validateFile(fileBusinessEntity.getFileId());
            filePath = fileEntity.getFilePath();
            fileName = fileEntity.getFileName();
            fileStorageId = fileEntity.getFileFilestorageId();
            fileOriginalName = fileEntity.getFileOriginalName();
        }

        // 验证仓储
        SysFileStorage fileStorageEntity = validateFileStorage(fileStorageId);
        String fileStoragePath = fileStorageEntity.getFileStoragePath();
        File file = new File(fileStoragePath + filePath + "\\" + fileName);
        if (file.exists()) {
            response.reset();
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/octet-stream");
            response.setHeader("Content-Disposition", "attachment;filename=" + fileOriginalName);

            byte[] buffer = new byte[1000];
            int len;

            try (OutputStream os = response.getOutputStream();
                 FileInputStream fis = new FileInputStream(file);
                 BufferedInputStream bis = new BufferedInputStream(fis)) {
                while ((len = bis.read(buffer)) > 0) {
                    os.write(buffer, 0, len);
                }
                os.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 创建目录命令
     *
     * @param dirName       目录
     * @param fileStorageId 文件仓储 ID
     * @param targetId      目标目录 ID
     * @param businessId    业务 ID
     * @return Map tokenInfo
     */
    public static HashMap<String, String> mkdirCommand(String dirName,
                                                       Long fileStorageId,
                                                       Long targetId,
                                                       Long businessId) {
        if (StrUtils.isNullOrEmpty(dirName)) {
            throw new IllegalArgumentException(AjaxExceptionEnum.DIR_NAME_IS_REQUIRED.getMsg());
        }

        // 验证仓储
        SysFileStorage fileStorageEntity = validateFileStorage(fileStorageId);

        SysFileBusiness targetFileBusinessEntity = null;
        if (targetId != null) {
            targetFileBusinessEntity = fileUtils.sysFileBusinessMapper.findSysFileBusinessById(targetId);
        }

        String fileVirtualPath = "";
        if (targetFileBusinessEntity != null) {
            fileVirtualPath = targetFileBusinessEntity.getFileVirtualPath();
        }

        // 创建文件业务实体
        SysFileBusiness sysFileBusiness = new SysFileBusiness();
        sysFileBusiness.setId(SEQUENCE_GENERATOR.nextId());
        sysFileBusiness.setFileFilestorageId(fileStorageId);
        sysFileBusiness.setFileBusiness(businessId);
        sysFileBusiness.setFileDirName(dirName);
        sysFileBusiness.setFileVirtualPath(fileVirtualPath + "\\" + dirName);

        // 创建树结构
        if (targetFileBusinessEntity != null) {
            sysFileBusiness.setFilePid(targetFileBusinessEntity.getId());
            if (StrUtils.isNullOrEmpty(targetFileBusinessEntity.getFilePidTree())) {
                sysFileBusiness.setFilePidTree(targetFileBusinessEntity.getId().toString());
            } else {
                sysFileBusiness.setFilePidTree(targetFileBusinessEntity.getFilePidTree() + "," + targetFileBusinessEntity.getId().toString());
            }
        }

        fileUtils.sysFileBusinessMapper.insertSysFileBusiness(sysFileBusiness);
        HashMap<String, String> resultInfo = new HashMap<String, String>();

        resultInfo.put("dirId", sysFileBusiness.getId().toString());
        resultInfo.put("dirName", sysFileBusiness.getFileDirName());
        resultInfo.put("src", fileStorageEntity.getFileStorageAccessPath() + sysFileBusiness.getFileVirtualPath());

        // 创建目录
        if (targetId == null) {
            String fileCompletedPath = fileStorageEntity.getFileStoragePath() + "\\" + dirName;
            createDir(fileCompletedPath);
        }
        return resultInfo;
    }

    /**
     * 重命名目录命令
     *
     * @param dirName  目录新名称
     * @param targetId 目标目录
     * @return Map tokenInfo
     */
    public static HashMap<String, String> renameCommand(String dirName,
                                                        Long targetId) {
        if (StrUtils.isNullOrEmpty(dirName)) {
            throw new IllegalArgumentException(AjaxExceptionEnum.DIR_NAME_IS_REQUIRED.getMsg());
        }

        // 验证目标目录文件业务关联
        SysFileBusiness targetFileBusinessEntity = validateFileBusiness(targetId);

        HashMap<String, String> info = new HashMap<>(4);
        List<SysFileBusiness> updateList = new ArrayList<>();
        String childPath;

        // 获取修改名称后的路径
        String fileVirtualPath = targetFileBusinessEntity.getFileVirtualPath();
        String newFileVirtualPath = getModifiedFilePath(targetFileBusinessEntity.getFilePidTree(), fileVirtualPath, targetId, dirName);

        // 修改文件业务关联路径
        targetFileBusinessEntity.setFileDirName(dirName);
        targetFileBusinessEntity.setFileVirtualPath(newFileVirtualPath);
        updateList.add(targetFileBusinessEntity);

        // 修改目录下的子文件目录的filePath
        List<SysFileBusiness> childList = fileUtils.sysFileBusinessMapper.findDirFilesByFilePid(targetId);
        for (SysFileBusiness child : childList) {
            childPath = getModifiedFilePath(child.getFilePidTree(), child.getFileVirtualPath(), targetId, dirName);
            child.setFileVirtualPath(childPath);
            updateList.add(child);
        }

        fileUtils.sysFileBusinessMapper.updateSysFileBusinesses(updateList);

        info.put("id", targetId.toString());
        info.put("oldPath", fileVirtualPath);
        info.put("newPath", newFileVirtualPath);

        return info;
    }






    /**
     * 复制目录和文件命令
     *
     * @param fileId   被复制目录的id
     * @param targetId 目标目录的id
     * @return Map tokenInfo
     */
    public static HashMap<String, String> copyCommand(Long fileId, Long targetId) {
        // 验证
        SysFileBusiness copyBusinessTarget = validateFileBusiness(fileId);
        SysFileBusiness targetBusinessDir = validateFileBusiness(targetId);
        String targetFilePath = targetBusinessDir.getFileVirtualPath();
        HashMap<String, String> info = new HashMap<>(4);

        // 获取复制后的路径
        String filePath = copyBusinessTarget.getFileVirtualPath();

        // 用户信息
        SysUser currUser = fileUtils.sysUserMapper.getUserByUsername(CurrentUserUtils.getOAuth2AuthenticationInfo().get("name"));

        // 递归完成目录和文件的保存
        copyRecursive(copyBusinessTarget, targetBusinessDir, currUser);

        info.put("id", targetId.toString());
        info.put("oldPath", filePath);
        info.put("newPath", targetFilePath);

        return info;
    }

    /**
     * 移动目录和文件命令
     *
     * @param fileId   被移动目录或文件的id
     * @param targetId 目标目录的id
     * @return Map tokenInfo
     */
    public static HashMap<String, String> moveCommand(Long fileId, Long targetId) {
        // 验证
        SysFileBusiness moveTarget = validateFileBusiness(fileId);
        SysFileBusiness targetDir = validateFileBusiness(targetId);

        String targetFilePath = targetDir.getFileVirtualPath();
        String targetFilePidTree = targetDir.getFilePidTree();
        HashMap<String, String> info = new HashMap<>(4);
        List<SysFileBusiness> moveList = new ArrayList<>();
        String childPath;
        String childPidTree;

        // 获取移动后的路径
        String filePath = moveTarget.getFileVirtualPath();
        String newFilePath = getCopiedFilePath(moveTarget.getFilePidTree(), filePath, fileId, targetFilePath);
        String newFilePidTree = getCopiedFilePidTree(moveTarget.getFilePidTree(), fileId, fileId, targetFilePidTree, targetId);

        moveTarget.setFileVirtualPath(newFilePath);
        moveTarget.setFilePid(targetId);
        moveTarget.setFilePidTree(newFilePidTree);
        moveList.add(moveTarget);

        // 修改目录下的子文件目录的filePath
        List<SysFileBusiness> childList = fileUtils.sysFileBusinessMapper.findDirFilesByFilePid(fileId);
        for (SysFileBusiness child : childList) {
            childPath = getCopiedFilePath(child.getFilePidTree(), child.getFileVirtualPath(), fileId, targetFilePath);
            childPidTree = getCopiedFilePidTree(child.getFilePidTree(), fileId, fileId, targetFilePidTree, targetId);
            child.setFileVirtualPath(childPath);
            child.setFilePidTree(childPidTree);
            moveList.add(child);
        }

        fileUtils.sysFileBusinessMapper.updateSysFileBusinesses(moveList);

        info.put("id", targetId.toString());
        info.put("oldPath", filePath);
        info.put("newPath", newFilePath);

        return info;
    }

    /**
     * 打开目录操作
     *
     * @param businessId 业务id
     * @param targetId   目标目录id
     * @param isReview   是否查询需要的审核文件
     * @return List<LinkedHashMap < String, Object>>
     */
    public static CurrDirFileMapDTO openDirCommand(Long businessId, Long targetId, boolean isReview) {
        if (businessId == null) {
            throw new IllegalArgumentException(AjaxExceptionEnum.ID_IS_REQUIRED.getMsg());
        }

        CurrDirFileMapDTO dirFileDTO = new CurrDirFileMapDTO();
        SysFileBusiness root;

        // 目标目录targetId不存在,则查询当前项目的根目录
        if (targetId == null) {
            root = fileUtils.sysFileBusinessMapper.findRootFileByFileBusiness(businessId);
        } else {
            root = fileUtils.sysFileBusinessMapper.findSysFileBusinessById(targetId);
        }
        dirFileDTO.setCurrDir(root);

        List<LinkedHashMap<String, Object>> results = fileUtils.sysFileBusinessMapper.findCurrDirFilesByFilePid(root.getId(), isReview);
        dirFileDTO.setList(results);
        return dirFileDTO;
    }

    /**
     * 搜索业务关联的文件
     *
     * @param businessId 业务id
     * @param fileName   搜索文件名称
     * @param isReview   是否查询需要的审核文件
     * @return List<LinkedHashMap < String, Object>>
     */
    public static CurrDirFileMapDTO searchFileCommand(Long businessId, String fileName, boolean isReview) {
        if (businessId == null) {
            throw new IllegalArgumentException(AjaxExceptionEnum.ID_IS_REQUIRED.getMsg());
        }
        Map<String, Object> paramMap = new HashMap<>();
        paramMap.put("fileBusiness", businessId);
        paramMap.put("isReview", isReview);
        if (PinyinUtils.isEnglish(fileName)) {
            paramMap.put("pinyin", fileName);
        } else {
            paramMap.put("fileOriginName", fileName);
        }

        CurrDirFileMapDTO dirFileDTO = new CurrDirFileMapDTO();
        SysFileBusiness root = fileUtils.sysFileBusinessMapper.findRootFileByFileBusiness(businessId);
        List<LinkedHashMap<String, Object>> results = fileUtils.sysFileBusinessMapper.findFilesByFileBusinessAndFileOriginalName(paramMap);

        dirFileDTO.setCurrDir(root);
        dirFileDTO.setList(results);
        return dirFileDTO;
    }

    /**
     * 创建文件工具
     *
     * @param file              接收到的文件
     * @param fileStorageEntity 文件仓储
     * @param filePath          存储目标的目录地址
     * @param extraParams       扩展参数，支持: fileMD5 auditRemark operatorUserId
     */
    public static void fileCreateUtil(MultipartFile file,
                                      String fileName,
                                      String filePath,
                                      SysFileStorage fileStorageEntity,
                                      HashMap<String, String> extraParams) {
        // 验证文件
        if (file.isEmpty()) {
            throw new IllegalArgumentException(AjaxExceptionEnum.FILE_IS_REQUIRED.getMsg());
        }

        // 验证文件名称
        if (StrUtils.isNullOrEmpty(fileName)) {
            throw new IllegalArgumentException(AjaxExceptionEnum.FILE_NAME_IS_REQUIRED.getMsg());
        }

        // 验证文件路径
        if (StrUtils.isNullOrEmpty(filePath)) {
            throw new IllegalArgumentException(AjaxExceptionEnum.FILE_PATH_IS_REQUIRED.getMsg());
        }

        // 验证仓储实体
        if (fileStorageEntity == null) {
            throw new IllegalArgumentException(AjaxExceptionEnum.FILE_STORAGE_NOT_FIND.getMsg());
        }

        // 获取仓储信息和文件原始信息
        String fileStoragePath = fileStorageEntity.getFileStoragePath();
        long size = file.getSize();

        // 保存文件上一级目录
        String fileCompletedPath = fileStoragePath + filePath + "/" + fileName;

        // 判断文件父目录是否存在
        File storageFile = new File(fileCompletedPath);
        if (!storageFile.getParentFile().exists()) {
            storageFile.getParentFile().mkdirs();
        }

        // imageScale
        String imageScale = null;
        if (extraParams != null && !StrUtils.isNullOrEmpty(extraParams.get("imageScale"))) {
            imageScale = extraParams.get("imageScale");
        }
        // imageScaleW
        String imageScaleW = null;
        if (extraParams != null && !StrUtils.isNullOrEmpty(extraParams.get("imageScaleW"))) {
            imageScaleW = extraParams.get("imageScaleW");
        }
        // imageScaleH
        String imageScaleH = null;
        if (extraParams != null && !StrUtils.isNullOrEmpty(extraParams.get("imageScaleH"))) {
            imageScaleH = extraParams.get("imageScaleH");
        }
        // imageQuality
        String imageQuality = null;
        if (extraParams != null && !StrUtils.isNullOrEmpty(extraParams.get("imageQuality"))) {
            imageQuality = extraParams.get("imageQuality");
        }
        // imageQualitySize
        String imageQualitySize = null;
        if (extraParams != null && !StrUtils.isNullOrEmpty(extraParams.get("imageQualitySize"))) {
            imageQualitySize = extraParams.get("imageQualitySize");
        }

        try {
            if (imageScale != null) {
                FileOutputStream fos = new FileOutputStream(storageFile);
                ImgUtil.scale(
                        file.getInputStream(),
                        fos,
                        Float.parseFloat(imageScale)
                );
                fos.close();
            } else if (imageScaleW != null) {
                BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
                int width = bufferedImage.getWidth();

                int tw = Integer.parseInt(imageScaleW);

                float scale = (float) tw / width;

                FileOutputStream fos = new FileOutputStream(storageFile);
                ImgUtil.scale(
                        file.getInputStream(),
                        fos,
                        scale//缩放比例
                );
                fos.close();
            } else if (imageScaleH != null) {
                BufferedImage bufferedImage = ImageIO.read(file.getInputStream());
                int height = bufferedImage.getHeight();

                int th = Integer.parseInt(imageScaleH);

                float scale = (float) th / height;

                FileOutputStream fos = new FileOutputStream(storageFile);
                ImgUtil.scale(
                        file.getInputStream(),
                        fos,
                        scale//缩放比例
                );
                fos.close();
            } else if (imageQuality != null) {
                FileOutputStream fos = new FileOutputStream(storageFile);
                Img.from(file.getInputStream())
                        .setQuality(Float.parseFloat(imageQuality))//压缩比率
                        .write(fos);
                fos.close();
            } else if (imageQualitySize != null) {
                int ts = Integer.parseInt(imageQualitySize);
                if ((new Double((double) size / 1024)).intValue() > ts) {
                    float quality = (float) ts / ((new Double((double) size / 1024)).intValue());

                    FileOutputStream fos = new FileOutputStream(storageFile);
                    Img.from(file.getInputStream())
                            .setQuality(quality)//压缩比率
                            .write(fos);
                    fos.close();
                } else {
                    file.transferTo(storageFile);
                }
            } else {
                file.transferTo(storageFile); //保存文件
            }
        } catch (IllegalStateException | IOException e) {
            throw new IllegalArgumentException(e.getMessage());
        }
    }

    /**
     * 文件上传工具
     *
     * @param file          接收到的文件
     * @param fileStorageId 文件仓储 ID
     * @param targetId      目标目录
     * @param businessId    业务ID
     * @param fileType      文件类型
     * @param extraParams   扩展参数，支持: fileMD5 auditRemark operatorUserId
     * @return Map tokenInfo
     */
    public static HashMap<String, String> fileUploadUtil(MultipartFile file,
                                                         Long fileStorageId,
                                                         Long targetId,
                                                         Long businessId,
                                                         String fileType,
                                                         HashMap<String, String> extraParams) {
        if (file.isEmpty()) {
            throw new IllegalArgumentException(AjaxExceptionEnum.FILE_IS_REQUIRED.getMsg());
        }

        // 验证仓储
        SysFileStorage fileStorageEntity = validateFileStorage(fileStorageId);

        // 验证目标目录文件业务关联
        SysFileBusiness targetFileBusinessEntity = validateFileBusiness(targetId);

        // 查询目标目录的根目录
        SysFileBusiness targetRootEntity = fileUtils.sysFileBusinessMapper.findRootFileByFileBusiness(businessId);

        // 获取仓储信息和文件原始信息
        String filePath = targetRootEntity.getFileVirtualPath();
        String fileVirtualPath = targetFileBusinessEntity.getFileVirtualPath();
        String fileStorageType = fileStorageEntity.getFileStorageType();
        String fileStorageAccessPath = fileStorageEntity.getFileStorageAccessPath();
        String fileOriginalName = file.getOriginalFilename();
        String[] fileOriginalNameArr = fileOriginalName.split("\\.");
        String fileExtension = fileOriginalNameArr[fileOriginalNameArr.length - 1].toLowerCase();
        String fileName = StrUtils.getUUID() + "." + fileExtension;
        long size = file.getSize();

        // 创建文件实体
        SysFile fileEntity = new SysFile();
        fileEntity.setId(SEQUENCE_GENERATOR.nextId());
        fileEntity.setFileFilestorageId(fileStorageId);
        fileEntity.setFileName(fileName);
        fileEntity.setFileOriginalName(fileOriginalName);
        fileEntity.setFilePath(filePath);
        fileEntity.setFileSize(size);
        fileEntity.setFileExtension(fileExtension);

        // 创建文件业务实体
        SysFileBusiness fileBusinessEntity = new SysFileBusiness();
        fileBusinessEntity.setId(SEQUENCE_GENERATOR.nextId());
        fileBusinessEntity.setFileId(fileEntity.getId());
        fileBusinessEntity.setFileFilestorageId(fileStorageId);
        fileBusinessEntity.setFileBusiness(businessId);
        fileBusinessEntity.setFileVirtualPath(fileVirtualPath);

        // 创建树结构
        fileBusinessEntity.setFilePid(targetFileBusinessEntity.getId());
        if (StrUtils.isNullOrEmpty(targetFileBusinessEntity.getFilePidTree())) {
            fileBusinessEntity.setFilePidTree(targetFileBusinessEntity.getId().toString());
        } else {
            fileBusinessEntity.setFilePidTree(targetFileBusinessEntity.getFilePidTree() + "," + targetFileBusinessEntity.getId().toString());
        }

        // 用户信息
        SysUser currUser = fileUtils.sysUserMapper.getUserByUsername(CurrentUserUtils.getOAuth2AuthenticationInfo().get("name"));
        fileEntity.setCreateName(currUser.getNickname());
        fileEntity.setCreateUsername(currUser.getUsername());

        String fileSource = "";
        if (extraParams != null) {
            fileSource = extraParams.get("fileSource");
        }

        // 文件业务来源
        if (!StrUtils.isNullOrEmpty(fileSource)) {
            fileBusinessEntity.setFileSource(fileSource);
        }

        // 文件自定义业务
        if (extraParams != null && !StrUtils.isNullOrEmpty(extraParams.get("fileBusiness"))) {
            fileEntity.setFileBusiness(Long.parseLong(extraParams.get("fileBusiness")));
        }

        // 文件自定义类别
        if (extraParams != null && !StrUtils.isNullOrEmpty(extraParams.get("fileCatalog"))) {
            fileEntity.setFileCatalog(extraParams.get("fileCatalog"));
        }

        // 插入文件，返回信息
        fileUtils.sysFileMapper.insertSysFile(fileEntity);
        fileUtils.sysFileBusinessMapper.insertSysFileBusiness(fileBusinessEntity);

        HashMap<String, String> fileSrc = new HashMap<>();
        if ("sys_file_storage_type_static".equals(fileStorageType)) {
            fileSrc.put("src", fileStorageAccessPath + filePath + "/" + fileName);
        } else {
            fileSrc.put("src", fileStorageAccessPath + "/score/file/download?id=" + fileEntity.getId());
        }
        fileSrc.put("filename", fileName);
        fileSrc.put("fileOriginalName", fileOriginalName);
        fileSrc.put("size", String.valueOf(size));
        fileSrc.put("fileExtension", fileExtension);
        fileSrc.put("fileId", fileEntity.getId().toString());

        // 创建文件
        fileCreateUtil(file, fileName, filePath, fileStorageEntity, extraParams);
        return fileSrc;
    }




    /**
     * 批量新增用户与文件关联数据
     *
     * @param users 用户id数组
     * @param files 文件id数组
     */
    public static void filesAssociatedUsers(List<Long> users, List<Long> files) {
        List<Long> userFile = new ArrayList<>();
        List<List<Long>> userFiles = new ArrayList<>();

        for (Long u : users) {
            for (Long f : files) {
                userFile.add(SEQUENCE_GENERATOR.nextId());
                userFile.add(u);
                userFile.add(f);
                userFiles.add(userFile);
            }
        }

        fileUtils.sysUserFileMapper.insertUsersFiles(userFiles);
    }

    /**
     * 批量新增用户与文件关联数据
     *
     * @param userGroups 用户组id数组
     * @param files      文件id数组
     */
    public static void filesAssociatedUserGroup(List<Long> userGroups, List<Long> files) {
        List<Long> userGroupFile = new ArrayList<>();
        List<List<Long>> userGroupFiles = new ArrayList<>();
        for (Long ug : userGroups) {
            for (Long f : files) {
                userGroupFile.add(SEQUENCE_GENERATOR.nextId());
                userGroupFile.add(ug);
                userGroupFile.add(f);
                userGroupFiles.add(userGroupFile);
            }
        }

        fileUtils.sysUserGroupFileMapper.insertUserGroupFiles(userGroupFiles);
    }

    /**
     * 批量删除用户与文件关联数据
     *
     * @param ids 文件id数组
     */
    public static void filesAssociatedUsersRemove(Long[] ids) {
        fileUtils.sysUserFileMapper.deleteFileAssociatedUserByFileIdArr(ids);
    }

    /**
     * 批量删除用户组与文件关联数据
     *
     * @param ids 文件id数组
     */
    public static void filesAssociatedUserGroupRemove(Long[] ids) {
        fileUtils.sysUserGroupFileMapper.deleteFileAssociatedUserGroupByFileIdArr(ids);
    }

    /**
     * 批量删除文件历史关联数据
     *
     * @param ids 文件id数组
     */
    public static void filesHistoryRemove(Long[] ids) {
        if (ids.length == 0) {
            return;
        }

        List<SysFileHistory> fileHistoryList;
        SysFileStorage sysFileStorage = null;
        File deleteFile;
        List<File> fileList = new ArrayList<>();
        String filePath;

        for (Long id : ids) {
            fileHistoryList = fileUtils.sysFileHistoryMapper.findSysFileHistoryByFileId(id);
            for (SysFileHistory sysFileHistory : fileHistoryList) {
                if (sysFileStorage == null) {
                    sysFileStorage = fileUtils.sysFileStorageMapper.findSysFileStorageById(sysFileHistory.getFileFilestorageId());
                }
                filePath = sysFileStorage.getFileStoragePath() + sysFileHistory.getFilePath() + "\\" + sysFileHistory.getFileName();
                deleteFile = new File(filePath);
                if (deleteFile.exists()) {
                    fileList.add(deleteFile);
                }
            }
            sysFileStorage = null;
        }
        fileUtils.sysFileHistoryMapper.deleteSysFileHistoryByFileIds(ids);

        //删除文件
        for (File f : fileList) {
            f.delete();
        }
    }


    /**
     * 验证文件仓储
     *
     * @param fileStorageId 文件仓储id
     * @return SysFileStorage
     */
    private static SysFileStorage validateFileStorage(Long fileStorageId) {
        if (fileStorageId == null) {
            throw new IllegalArgumentException(AjaxExceptionEnum.FILE_STORAGE_ID_IS_REQUIRED.getMsg());
        }
        SysFileStorage fileStorageEntity = fileUtils.sysFileStorageMapper.findSysFileStorageById(fileStorageId);
        if (fileStorageEntity == null) {
            throw new IllegalArgumentException(AjaxExceptionEnum.FILE_STORAGE_NOT_FIND_BY_ID.getMsg());
        }
        return fileStorageEntity;
    }

    /**
     * 验证文件
     *
     * @param fileId 文件 ID
     * @return SysFile
     */
    private static SysFile validateFile(Long fileId) {
        if (fileId == null) {
            throw new IllegalArgumentException(AjaxExceptionEnum.ID_IS_REQUIRED.getMsg());
        }
        SysFile fileEntity = fileUtils.sysFileMapper.findSysFileById(fileId);
        if (fileEntity == null) {
            throw new IllegalArgumentException(AjaxExceptionEnum.ENTITY_NOT_FIND_BY_ID.getMsg());
        }
        return fileEntity;
    }

    /**
     * 验证文件历史
     *
     * @param id 文件历史 ID
     * @return SysFileHistory
     */
    private static SysFileHistory validateFileHistory(Long id) {
        if (id == null) {
            throw new IllegalArgumentException(AjaxExceptionEnum.ID_IS_REQUIRED.getMsg());
        }
        SysFileHistory fileHistoryEntity = fileUtils.sysFileHistoryMapper.findSysFileHistoryById(id);
        if (fileHistoryEntity == null) {
            throw new IllegalArgumentException(AjaxExceptionEnum.ENTITY_NOT_FIND_BY_ID.getMsg());
        }
        return fileHistoryEntity;
    }

    /**
     * 验证文件业务关联
     *
     * @param id 文件业务 ID
     * @return SysFileBusiness
     */
    private static SysFileBusiness validateFileBusiness(Long id) {
        if (id == null) {
            throw new IllegalArgumentException(AjaxExceptionEnum.ID_IS_REQUIRED.getMsg());
        }
        SysFileBusiness fileBusinessEntity = fileUtils.sysFileBusinessMapper.findSysFileBusinessById(id);
        if (fileBusinessEntity == null) {
            throw new IllegalArgumentException(AjaxExceptionEnum.ENTITY_NOT_FIND_BY_ID.getMsg());
        }
        return fileBusinessEntity;
    }

    /**
     * 创建目录
     *
     * @param destDirName 目录地址
     */
    private static void createDir(String destDirName) {
        File dir = new File(destDirName);
        if (dir.exists()) {
            // 判断目录是否存在
            System.out.println("创建目录失败，目标目录已存在！");
            throw new IllegalArgumentException(AjaxExceptionEnum.DIR_ALREADY_EXISTS.getMsg());
        }
        if (!destDirName.endsWith(File.separator)) {
            // 结尾是否以"/"结束
            destDirName = destDirName + File.separator;
        }
        if (dir.mkdirs()) {
            // 创建目标目录
            System.out.println("创建目录成功！" + destDirName);
        } else {
            System.out.println("创建目录失败！");
            throw new IllegalArgumentException(AjaxExceptionEnum.DIR_CREATE_FAILURE.getMsg());
        }
    }

    /**
     * 重命名目录
     *
     * @param destDirName 目录地址
     * @param dirName     重命名
     */
    private static void renameDir(String destDirName, String dirName) {
        // 判断旧目录是否存在
        File dir = new File(destDirName);
        if (!dir.exists()) {
            throw new IllegalArgumentException(AjaxExceptionEnum.DIR_NOT_FIND.getMsg());
        }

        // 更新目录地址
        int last = destDirName.lastIndexOf("\\");
        String newDestDirName = destDirName.substring(0, last + 1) + dirName;

        // 判断新目录是否存在
        File newDir = new File(newDestDirName);
        if (newDir.exists()) {
            System.out.println("重命名目录失败，目标目录已存在！");
            throw new IllegalArgumentException(AjaxExceptionEnum.DIR_ALREADY_EXISTS.getMsg());
        }
        dir.renameTo(newDir);
    }

    /**
     * 复制整个目录内容或文件
     *
     * @param sourceFileOrDir 原文件或目录
     * @param targetDir       目标目录
     * @param isDelete        是否删除
     */
    private static void copyDir(File sourceFileOrDir, File targetDir, Boolean isDelete) {
        if (!sourceFileOrDir.exists() || !targetDir.exists()) {
            throw new IllegalArgumentException(AjaxExceptionEnum.DIR_NOT_FIND.getMsg());
        }
        if (sourceFileOrDir.getAbsolutePath().equals(targetDir.getAbsolutePath())) {
            throw new IllegalArgumentException(AjaxExceptionEnum.DIR_PATH_IS_SAME.getMsg());
        }

        // 递归创建目录，如果是目录则创建并递归，否则创建文件
        File newTargetFile = new File(targetDir.getAbsolutePath() + File.separator + sourceFileOrDir.getName());
        if (sourceFileOrDir.isDirectory()) {
            newTargetFile.mkdir();
            for (File item : Objects.requireNonNull(sourceFileOrDir.listFiles())) {
                FileUtils.copyDir(item, newTargetFile, isDelete);
            }
            if (isDelete) {
                sourceFileOrDir.delete();
            }
            return;
        }

        // 创建文件
        try (FileInputStream fis = new FileInputStream(sourceFileOrDir); FileOutputStream fos = new FileOutputStream(newTargetFile)) {
            byte[] buff = new byte[1024];
            int len;
            while ((len = fis.read(buff)) != -1) {
                fos.write(buff, 0, len);
            }
            fis.close();
            fos.close();
            if (isDelete) {
                sourceFileOrDir.delete();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 递归更新复制目录或文件的信息，并保存与文件相关的数据，该方法关联业务的逻辑数据，业务的逻辑数据关联唯一的原始数据，不需要再次关联相关表
     *
     * @param copyTarget 被复制的目录或文件（原始数据）
     * @param dirTarget  目标目录
     * @param currUser   操作人员
     */
    private static void copyRecursive(SysFileBusiness copyTarget, SysFileBusiness dirTarget, SysUser currUser) {
        if (copyTarget == null || dirTarget == null) {
            throw new IllegalArgumentException(AjaxExceptionEnum.INTERNAL_ENTITY_NOT_FIND.getMsg());
        }
        if (currUser == null) {
            throw new IllegalArgumentException(AjaxExceptionEnum.USER_NOT_FIND.getMsg());
        }

        // 原始数据
        Long fileId = copyTarget.getId();
        Long targetId = dirTarget.getId();
        String childPath;
        String childPidTree;
        String filePath = copyTarget.getFileVirtualPath();
        String targetFilePath = dirTarget.getFileVirtualPath();
        String targetFilePidTree = dirTarget.getFilePidTree();

        copyTarget.setId(SEQUENCE_GENERATOR.nextId());
        Long newCopyTargetId = copyTarget.getId();

        // 获取复制后的路径
        String newFilePath = getCopiedFilePath(copyTarget.getFilePidTree(), filePath, fileId, targetFilePath);
        String newFilePidTree;

        // 文件不需要父id树新增
        if (copyTarget.getFileId() == null) {
            newFilePidTree = getCopiedFilePidTree(copyTarget.getFilePidTree(), fileId, newCopyTargetId, targetFilePidTree, targetId);
        } else {
            newFilePidTree = getCopiedFilePidTree(copyTarget.getFilePidTree(), fileId, null, targetFilePidTree, targetId);
        }

        copyTarget.setFileVirtualPath(newFilePath);
        copyTarget.setFilePid(targetId);
        copyTarget.setFilePidTree(newFilePidTree);

        fileUtils.sysFileBusinessMapper.insertSysFileBusiness(copyTarget);
        if (copyTarget.getFileId() == null) {

            // 遍历目录下的下级文件：如果是目录，则递归；如果是子文件，修改子文件，并新增记录
            List<SysFileBusiness> childList = fileUtils.sysFileBusinessMapper.findFileBusinessByFilePid(fileId);
            for (SysFileBusiness child : childList) {
                if (child.getFileId() == null) {
                    copyRecursive(child, copyTarget, currUser);
                } else {
                    child.setId(SEQUENCE_GENERATOR.nextId());

                    // 获取复制后的路径
                    childPath = getCopiedFilePath(child.getFilePidTree(), child.getFileVirtualPath(), fileId, targetFilePath);

                    // 文件不需要父id树新增
                    if (child.getFileId() == null) {
                        childPidTree = getCopiedFilePidTree(child.getFilePidTree(), fileId, child.getId(), targetFilePidTree, targetId);
                    } else {
                        childPidTree = getCopiedFilePidTree(child.getFilePidTree(), fileId, null, targetFilePidTree, targetId);
                    }

                    child.setFileVirtualPath(childPath);
                    child.setFilePid(newCopyTargetId);
                    child.setFilePidTree(childPidTree);

                    fileUtils.sysFileBusinessMapper.insertSysFileBusiness(child);
                }
            }
        }
    }

    /**
     * 生成日期作为文件路径
     *
     * @param pathType pathType 目前只实现了 default，按日期组织文件目录
     * @return 文件路径
     */
    private static String createFilePath(String pathType) {
        if (StrUtils.isNullOrEmpty(pathType)) {
            pathType = "default";
        }

        String returnPath = "/";

        if (pathType.equals("default")) {
            Calendar c = Calendar.getInstance(); // 可以对每个时间域单独修改
            int year = c.get(Calendar.YEAR);
            int month = c.get(Calendar.MONTH);
            int date = c.get(Calendar.DATE);
            returnPath += year + "/" + (month + 1) + "/" + date;
        }

        return returnPath;
    }

    /**
     * 获取重命名后的filePath
     *
     * @param filePidTree      重命名目录下的文件父id树
     * @param filePath         重命名目录下的文件路径
     * @param renameFileRootId 重命名目录id，重命名目录下的文件的根节点id
     * @param dirName          修改后名称
     * @return String
     */
    private static String getModifiedFilePath(String filePidTree, String filePath, Long renameFileRootId, String dirName) {
        if (filePidTree == null || StrUtils.isNullOrEmpty(filePidTree)) {
            return "\\" + dirName;
        }
        if (renameFileRootId == null) {
            throw new IllegalArgumentException(AjaxExceptionEnum.ID_IS_REQUIRED.getMsg());
        }

        StringBuilder newPath = new StringBuilder();
        List<String> treeList = Arrays.asList(filePidTree.split(","));
        List<String> pathList = Arrays.asList(filePath.split("\\\\"));
        int index = treeList.indexOf(renameFileRootId.toString());

        if (index > -1) {
            // pathList分割后第一个是空
            for (int i = 1; i < pathList.size(); i++) {
                newPath.append("\\");
                if (i == index + 1) {
                    newPath.append(dirName);
                } else {
                    newPath.append(pathList.get(i));
                }
            }
        } else {
            index = filePath.lastIndexOf("\\");
            newPath.append(filePath, 0, index + 1);
            newPath.append(dirName);
        }

        return newPath.toString();
    }

    /**
     * 获取复制后的filePath
     *
     * @param copiedFilePidTree 复制目录下的文件父id树
     * @param copiedFilePath    复制目录下的文件路径
     * @param copiedFileRootId  被复制目录id，其他被移动文件的根节点id
     * @param targetFilePath    目标文件路径
     * @return String
     */
    private static String getCopiedFilePath(String copiedFilePidTree, String copiedFilePath, Long copiedFileRootId, String targetFilePath) {
        if (copiedFileRootId == null) {
            throw new IllegalArgumentException(AjaxExceptionEnum.ID_IS_REQUIRED.getMsg());
        }
        StringBuilder newPath = new StringBuilder();
        String finalPath;
        List<String> treeList = Arrays.asList(copiedFilePidTree.split(","));
        List<String> pathList = Arrays.asList(copiedFilePath.split("\\\\"));
        int index = treeList.indexOf(copiedFileRootId.toString());

        if (index > -1) {
            // pathList分割后第一个是空
            for (int i = 1; i < pathList.size(); i++) {
                if (i >= index + 1) {
                    newPath.append("\\");
                    newPath.append(pathList.get(i));
                }
            }
        } else {
            index = copiedFilePath.lastIndexOf("\\");
            newPath.append(copiedFilePath.substring(index));
        }

        finalPath = targetFilePath + newPath.toString();
        return finalPath;
    }

    /**
     * 获取复制后的filePidTree
     *
     * @param copiedFilePidTree   复制目录下的文件父id树
     * @param copiedFileRootId    被复制目录id，需要未更新id之前的目录或文件id
     * @param copiedFileRootNewId 被复制目录产生的新id，如果是文件，该id为null
     * @param targetFilePidTree   目标文件父id树
     * @param targetFileId        目标文件id
     * @return String
     */
    private static String getCopiedFilePidTree(String copiedFilePidTree, Long copiedFileRootId, Long copiedFileRootNewId, String targetFilePidTree, Long targetFileId) {
        if (copiedFileRootId == null || targetFileId == null) {
            throw new IllegalArgumentException(AjaxExceptionEnum.ID_IS_REQUIRED.getMsg());
        }
        StringBuilder newFilePidTree = new StringBuilder();
        String finalFilePidTree;

        // 用老的文件父id找到目标位置，然后替换成新的父id
        List<String> treeList = Arrays.asList(copiedFilePidTree.split(","));
        int index = treeList.indexOf(copiedFileRootId.toString());

        if (index > -1) {
            for (int i = 1; i < treeList.size(); i++) {
                if (copiedFileRootNewId != null && i == index) {
                    newFilePidTree.append(",");
                    newFilePidTree.append(copiedFileRootNewId);
                } else if (i > index) {
                    newFilePidTree.append(",");
                    newFilePidTree.append(treeList.get(i));
                }
            }
        }

        if (!StrUtils.isNullOrEmpty(targetFilePidTree)) {
            finalFilePidTree = targetFilePidTree + "," + targetFileId.toString() + newFilePidTree.toString();
        } else {
            finalFilePidTree = targetFileId.toString() + newFilePidTree.toString();
        }

        return finalFilePidTree;
    }

    /**
     * 文件转byte字节数组
     *
     * @param file 文件
     * @return byte字节数组
     */
    private static byte[] fileConvertToByteArray(File file) {
        byte[] data = null;

        try {
            FileInputStream fis = new FileInputStream(file);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();

            int len;
            byte[] buffer = new byte[1024];
            while ((len = fis.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }

            data = baos.toByteArray();

            fis.close();
            baos.close();
            baos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return data;
    }
}
